package com.xand.bt.terminal.btce.connector.market;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.xand.bt.terminal.common.api.MarketDepthUpdate;

public class MarketStore {

	private Map<String, MarketReference> referencesHashedByName;
	
	private Map<String, MarketReferenceDepth> marketDepthHashedByReference;
	
	private List<MarketDepthUpdate> currentUpdates;
	
	private Map<String, MarketTicker> marketTickerHashedByReference;
	
	public MarketStore() {
		this.referencesHashedByName = new HashMap<String, MarketReference>();
		this.marketDepthHashedByReference = new HashMap<String, MarketReferenceDepth>();
		this.currentUpdates = new ArrayList<MarketDepthUpdate>();
		this.marketTickerHashedByReference = new HashMap<String, MarketTicker>();
	}
	
	public synchronized List<MarketDepthUpdate> getCurrentUpdates() {
		
		List<MarketDepthUpdate> ret = new ArrayList<MarketDepthUpdate>();
		
		for(MarketDepthUpdate singleUpdate : this.currentUpdates) {
			
			ret.add(
					new MarketDepthUpdate(
							singleUpdate.getUpdateType(), 
							singleUpdate.getReference(), 
							singleUpdate.getDirection(), 
							singleUpdate.getPrice(), 
							singleUpdate.getVolume()));
			
		}
		
		this.currentUpdates.clear();
		
		return ret;
	}
	
	public synchronized void addMarketReference(MarketReference marketReference) {
		this.referencesHashedByName.put(marketReference.getName(), marketReference);
		this.marketDepthHashedByReference.put(marketReference.getName(), new MarketReferenceDepth(marketReference.getName()));
	}
	
	public MarketReference getMarketReference(String name) {
		return this.referencesHashedByName.get(name);
	}
	
	public synchronized void addMarketLevel(MarketLevel marketLevel) {
		
		List<MarketLevel> levels = this.getOrderedLevelsForReferenceAndDirection(marketLevel.getReference(), marketLevel.getDirection());
		Map<Long, MarketLevel> levelsHashedByPrice = this.getHashedLevelsForReferenceAndDirection(marketLevel.getReference(), marketLevel.getDirection());
		
		levels.add(marketLevel);
		Collections.sort(levels);
		
		levelsHashedByPrice.put(marketLevel.getPrice(), marketLevel);
		
		this.addMarketUpdate(MarketDepthUpdate.UPDATE_TYPE_ADD_LEVEL, marketLevel.getReference(), 
				marketLevel.getDirection(), marketLevel.getPrice(), marketLevel.getVolume());
		
	}
	
	public synchronized void updateMarketLevelVolume(String reference, int direction, long price, long newVolume) {
		
		Map<Long, MarketLevel> levelsHashedByPrice = this.getHashedLevelsForReferenceAndDirection(reference, direction);
		MarketLevel marketLevel = levelsHashedByPrice.get(price);
		
		marketLevel.setVolume(newVolume);
		
		this.addMarketUpdate(MarketDepthUpdate.UPDATE_TYPE_UPDATE_LEVEL_VOLUME, marketLevel.getReference(), 
				marketLevel.getDirection(), marketLevel.getPrice(), marketLevel.getVolume());
		
	}
	
	public synchronized void removeMarketLevel(String reference, int direction, long price) {
		
		List<MarketLevel> levels = this.getOrderedLevelsForReferenceAndDirection(reference, direction);
		Map<Long, MarketLevel> levelsHashedByPrice = this.getHashedLevelsForReferenceAndDirection(reference, direction);
		
		int levelNumToRemove = 0;
		for(int i = 0; i < levels.size(); i++) {
			
			MarketLevel level = levels.get(i);
			if(level.getPrice() == price) {
				levelNumToRemove = i;
				break;
			}
		}
		
		levels.remove(levelNumToRemove);
		levelsHashedByPrice.remove(price);
		
		this.addMarketUpdate(MarketDepthUpdate.UPDATE_TYPE_REMOVE_LEVEL, reference, 
				direction, price, 0);
		
	}
	
	public MarketLevel getMarketLevel(String reference, int direction, long price) {
		
		Map<Long, MarketLevel> levelsHashedByPrice = this.getHashedLevelsForReferenceAndDirection(reference, direction);
		MarketLevel marketLevel = levelsHashedByPrice.get(price);
		
		return marketLevel;
	}
	
	public List<Long> getPricesForReferenceAndDirection(String reference, int direction) {
		
		List<MarketLevel> levels = this.getOrderedLevelsForReferenceAndDirection(reference, direction);
		
		List<Long> ret = new ArrayList<Long>();
		
		for(MarketLevel level : levels) {
			ret.add(level.getPrice());
		}
		
		return ret;
	}
	
	private void addMarketUpdate(int updateType, String reference, int direction, long price, long volume) {
		
		int updateDirection;
		if(direction == MarketLevel.DIRECTION_ASK) {
			updateDirection = MarketDepthUpdate.DIRECTION_ASK;
		} else {
			updateDirection = MarketDepthUpdate.DIRECTION_BID;
		}
		
		MarketDepthUpdate marketDepthUpdate = new MarketDepthUpdate(
				updateType, reference, updateDirection, price, volume);
		
		this.currentUpdates.add(marketDepthUpdate);
		
	}
	
	private List<MarketLevel> getOrderedLevelsForReferenceAndDirection(String reference, int direction) {
		
		List<MarketLevel> ret = null;
		
		MarketReferenceDepth depth = this.marketDepthHashedByReference.get(reference);
		
		if(direction == MarketLevel.DIRECTION_ASK) {
			ret = depth.getAsks();
		}else{
			ret = depth.getBids();
		}
		
		return ret;
	}
	
	private Map<Long, MarketLevel> getHashedLevelsForReferenceAndDirection(String reference, int direction) {
		
		Map<Long, MarketLevel> ret = null;
		
		MarketReferenceDepth depth = this.marketDepthHashedByReference.get(reference);
		
		if(direction == MarketLevel.DIRECTION_ASK) {
			ret = depth.getAsksHashedByPrice();
		}else{
			ret = depth.getBidsHashedByPrice();
		}
		
		return ret;
	}
	
}
